{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 0
   },
   "source": [
    "# 注意力提示\n",
    ":label:`sec_attention-cues`\n",
    "\n",
    "感谢你对本书的关注。注意力是一种稀缺资源：此时你正在读本书，而忽略其他内容。因此，与金钱类似，你的注意力也伴随着机会成本。为了确保你现在的注意力投入是值得的，我们一直被被高度激励着用心去创作一本好书。注意力是生命拱门的基石，是任何作品出类拔萃的关键所在。\n",
    "\n",
    "根据经济学研究中稀缺资源的分配，我们正处于处于注意力经济时代。在这里，人类的注意力被视为一种有限的、有价值的、稀缺的、可以进行交换的商品。人们利用它开发了许多商业模式。在音视频流媒体服务中，我们要么关注他们的广告，要么花钱来隐藏它们。为了在网络游戏世界成长，我们要么付出注意力参与战斗，吸引新手玩家，要么花钱立即变得强大。天下没有免费的午餐。\n",
    "\n",
    "总而言之，稀缺的并不是我们环境中的信息而是注意力。在注视一个视觉场景时，我们的光学神经以每秒 $10^8$ bits 的速度接收信息，远远超过我们的大脑可以完全处理的信息。幸运的是，我们的祖先已经从经验（也称为数据）中了解到*并非所有的感官输入都是平等的*。纵观人类历史，将注意力集中在感兴趣的一小部分信息的能力，使我们的大脑能够更聪明地分配资源以生存、成长和社交，例如探测捕食者、猎物和配偶。\n",
    "\n",
    "\n",
    "## 生物学中的注意力提示\n",
    "\n",
    "为了解释我们的注意力是如何在视觉世界中被部署的，一个双组件（two-component）的框架应运而生，并得到了普及。这个观点可以追溯到1890年代的 William James，他被认为是“美国心理学之父”:cite:`James.2007`。在这个框架里，受试者利用 *非自主性提示（nonvolitional cue）* 和 *自主性提示（volitional cue）* 有选择地引导注意力的焦点。\n",
    "\n",
    "非自主性提示是基于环境中物体的突出性和易见性。想象你面前有五样东西：一份报纸，一份研究报告，一杯咖啡，一个笔记本和一本书，如 :numref:`fig_eye-coffee` 所示。所有的纸制品都是黑白印刷的，而咖啡杯是红色的。换句话说，这杯咖啡在这个视觉环境中本质上是很突出和明显的，自动地和不自觉地吸引人们的注意力。所以你把fovea（黄斑中心，视力最敏锐的地方）放在咖啡上，如 :numref:`fig_eye-coffee` 所示。\n",
    "\n",
    "![使用基于突出性的非自主性提示（红杯子，而非纸张），注意力不自主地指向了咖啡。](https://zh-v2.d2l.ai/_images/eye-coffee.svg)\n",
    ":width:`400px`\n",
    ":label:`fig_eye-coffee`\n",
    "\n",
    "喝完咖啡后，你会变得兴奋并想读书。所以你转过头，重新聚焦你的眼睛，然后看着书，如 :numref:`fig_eye-book` 所示。在 :numref:`fig_eye-coffee` 的案例中，咖啡会让你基于显著性选择，而在这个任务依赖的案例中，你会在认知和意志控制下选择书籍。使用基于可变选择标准的意志线索，这种形式的注意力更刻意。主体的自主意愿也更强大。\n",
    "\n",
    "\n",
    "![通过使用依赖于任务的意志提示（想读一本书），注意力被自主引导的书上。](https://zh-v2.d2l.ai/_images/eye-book.svg)\n",
    ":width:`400px`\n",
    ":label:`fig_eye-book`\n",
    "\n",
    "\n",
    "## 查询、键和值\n",
    "\n",
    "自主性的与非自主性的注意力提示解释了注意力的方式，下面我们将描述设计注意力机制时的框架，框架中合并这两个注意力提示来设计注意力机制。\n",
    "\n",
    "首先，考虑一个相对简单的状况，即只使用非自主性提示。要想将选择偏向于感官输入，我们可以简单地使用参数化的全连接层，甚至是非参数化的最大汇聚层或平均汇聚层。\n",
    "\n",
    "因此，通过是否包含自主性提示将注意力机制与全连接层或汇聚层区别开来。在注意力机制的背景下，我们将自主性提示称为 *查询（Queries）*。给定任何查询，注意力机制通过 *注意力汇聚（attention pooling）* 将选择偏向于感官输入（例如中间特征表示）。在注意力机制的背景下，这些感官输入被称为 *值（Values）*。更通俗的解释，每个值都与一个 *键（Keys）* 配对，这可以想象为感官输入的非自主提示。如 :numref:`fig_qkv` 所示，我们可以设计注意力汇聚，以便给定的查询（自主性提示）可以与键（非自主性提示）进行交互，这将引导将选择偏向于值（感官输入）。\n",
    "\n",
    "![注意力机制通过注意力汇聚将 查询（自主性提示）和 键（非自主性提示）结合在一起，实现对 值（感官输入）的选择倾向。](https://zh-v2.d2l.ai/_images/qkv.svg)\n",
    ":label:`fig_qkv`\n",
    "\n",
    "注意，注意力机制的设计有许多替代方案。例如，我们可以设计一个不可微的注意力模型，该模型可以使用强化学习方法 :cite:`Mnih.Heess.Graves.ea.2014` 进行训练。鉴于上面所提框架在 :numref:`fig_qkv` 中的主导地位，因此这个框架下的模型将成为本章我们关注的中心。\n",
    "\n",
    "\n",
    "## 注意力的可视化\n",
    "\n",
    "平均汇聚层可以被视为输入的加权平均值，其中各输入的权重是一样的。实际上，注意力汇聚得到的是加权平均的总和值，其中权重是在给定的查询和不同的键之间计算得出的。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%load ../utils/djl-imports\n",
    "%load ../utils/plot-utils\n",
    "%load ../utils/Functions.java"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "NDManager manager = NDManager.newBaseManager();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 4
   },
   "source": [
    "为了可视化注意力权重，我们定义了 show_heatmaps 函数。它的输入矩阵的形状（要显示的行数，要显示的列数，查询的数量，键的数量）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "public static Figure showHeatmaps(\n",
    "            NDArray matrices,\n",
    "            String xLabel,\n",
    "            String yLabel,\n",
    "            String[] titles,\n",
    "            int width,\n",
    "            int height) {\n",
    "    int numRows = (int) matrices.getShape().get(0);\n",
    "    int numCols = (int) matrices.getShape().get(1);\n",
    "\n",
    "    Trace[] traces = new Trace[numRows * numCols];\n",
    "    int count = 0;\n",
    "    for (int i = 0; i < numRows; i++) {\n",
    "        for (int j = 0; j < numCols; j++) {\n",
    "            NDArray NDMatrix = matrices.get(i).get(j);\n",
    "            double[][] matrix =\n",
    "                    new double[(int) NDMatrix.getShape().get(0)]\n",
    "                            [(int) NDMatrix.getShape().get(1)];\n",
    "            Object[] x = new Object[matrix.length];\n",
    "            Object[] y = new Object[matrix.length];\n",
    "            for (int k = 0; k < NDMatrix.getShape().get(0); k++) {\n",
    "                matrix[k] = Functions.floatToDoubleArray(NDMatrix.get(k).toFloatArray());\n",
    "                x[k] = k;\n",
    "                y[k] = k;\n",
    "            }\n",
    "            HeatmapTrace.HeatmapBuilder builder = HeatmapTrace.builder(x, y, matrix);\n",
    "            if (titles != null) {\n",
    "                builder = (HeatmapTrace.HeatmapBuilder) builder.name(titles[j]);\n",
    "            }\n",
    "            traces[count++] = builder.build();\n",
    "        }\n",
    "    }\n",
    "    Grid grid =\n",
    "            Grid.builder()\n",
    "                    .columns(numCols)\n",
    "                    .rows(numRows)\n",
    "                    .pattern(Grid.Pattern.INDEPENDENT)\n",
    "                    .build();\n",
    "    Layout layout =\n",
    "            Layout.builder()\n",
    "                    .title(\"\")\n",
    "                    .xAxis(Axis.builder().title(xLabel).build())\n",
    "                    .yAxis(Axis.builder().title(yLabel).build())\n",
    "                    .width(width)\n",
    "                    .height(height)\n",
    "                    .grid(grid)\n",
    "                    .build();\n",
    "    return new Figure(layout, traces);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 6
   },
   "source": [
    "我们使用一个简单的例子进行演示。在本例子中，仅当查询和键相同时，注意力权重为1，否则为0。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "NDArray attentionWeights = manager.eye(10).reshape(new Shape(1, 1, 10, 10));\n",
    "showHeatmaps(attentionWeights, \"Keys\", \"Queries\", null, 700, 1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "origin_pos": 8
   },
   "source": [
    "在后面的章节中，我们将经常调用这个函数来可视化注意力权重。\n",
    "\n",
    "\n",
    "## 小结\n",
    "\n",
    "* 人类的注意力是一种有限的、有价值的、稀缺的资源。\n",
    "* 主体使用非自主性和自主性提示有选择性地引导注意力。前者是基于突出性，后者则依赖于任务。\n",
    "* 注意力机制和全连接层或池化层的区别源于增加的自主提示。\n",
    "* 注意力机制通过注意力汇聚使选择偏向于值（感官输入），其中包括查询（自主性提示）和键（非自主性提示）。键和值时成对的。\n",
    "* 我们可以可视化查询和键之间的注意力权重。\n",
    "\n",
    "\n",
    "## 练习\n",
    "\n",
    "1. 在机器翻译中通过解码序列词元时，其自主性提示可能是什么？非自主性提示和感官输入又是什么？\n",
    "1. 随机生成一个 $10 \\times 10$ 矩阵并使用 softmax 运算来确保每行都是有效的概率分布，然后可视化输出注意力权重。\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Java",
   "language": "java",
   "name": "java"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "Java",
   "pygments_lexer": "java",
   "version": "14.0.2+12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
